package utils

import (
	"context"
	"errors"
	"time"

	"github.com/golang-jwt/jwt/v4"
)

// 定义JWT相关常量
const (
	// TokenExpireDuration token过期时间
	TokenExpireDuration = time.Hour * 24
	// TokenRedisPrefix Redis中存储token的前缀
	TokenRedisPrefix = "token:"
)

// 定义错误信息
const (
	ErrInvalidToken  = "无效的token"
	ErrTokenExpired  = "token已过期"
	ErrTokenNotFound = "token不存在"
	ErrLoginFailed   = "用户名或密码错误"
	ErrTokenRequired = "需要认证token"
	ErrUnauthorized  = "未授权访问"
	ErrLoginSuccess  = "登录成功"
	ErrLogoutSuccess = "登出成功"
)

// CustomClaims 自定义JWT声明结构
type CustomClaims struct {
	UserID   string `json:"user_id"`
	Username string `json:"username"`
	Email    string `json:"email"`
	jwt.RegisteredClaims
}

// 密钥
var jwtSecret = []byte("intelli-dl-online-logo-secret-key")

// GenerateToken 生成JWT token
func GenerateToken(userID, username, email string) (string, error) {
	// 创建声明
	claims := CustomClaims{
		UserID:   userID,
		Username: username,
		Email:    email,
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(TokenExpireDuration)),
			IssuedAt:  jwt.NewNumericDate(time.Now()),
			Subject:   "user_token",
		},
	}

	// 创建token
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

	// 签名token
	tokenString, err := token.SignedString(jwtSecret)
	if err != nil {
		return "", err
	}

	// 将token存入Redis
	redisKey := TokenRedisPrefix + userID
	err = Set(redisKey, tokenString, TokenExpireDuration)
	if err != nil {
		return "", err
	}

	return tokenString, nil
}

// ParseToken 解析JWT token
func ParseToken(tokenString string) (*CustomClaims, error) {
	// 解析token
	token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
		return jwtSecret, nil
	})

	if err != nil {
		if ve, ok := err.(*jwt.ValidationError); ok {
			if ve.Errors&jwt.ValidationErrorExpired != 0 {
				return nil, errors.New(ErrTokenExpired)
			}
		}
		return nil, errors.New(ErrInvalidToken)
	}

	// 验证token
	if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
		// 检查Redis中是否存在该token
		redisKey := TokenRedisPrefix + claims.UserID
		existingToken, err := Get(redisKey)
		if err != nil || existingToken != tokenString {
			return nil, errors.New(ErrTokenNotFound)
		}

		return claims, nil
	}

	return nil, errors.New(ErrInvalidToken)
}

// RemoveToken 从Redis中删除token
func RemoveToken(userID string) error {
	redisKey := TokenRedisPrefix + userID
	return RedisClient.Del(context.Background(), redisKey).Err()
}
